{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "af6c7afe-1037-41ab-98e4-494692e47402",
      "metadata": {
        "id": "af6c7afe-1037-41ab-98e4-494692e47402"
      },
      "source": [
        "# Chatbot with message summarization & external DB memory\n",
        "\n",
        "## Review\n",
        "\n",
        "We've covered how to customize graph state schema and reducer.\n",
        "\n",
        "We've also shown a number of tricks for trimming or filtering messages in graph state.\n",
        "\n",
        "We've used these concepts in a Chatbot with memory that produces a running summary of the conversation.\n",
        "\n",
        "## Goals\n",
        "\n",
        "But, what if we want our Chatbot to have memory that persists indefinitely?\n",
        "\n",
        "Now, we'll introduce some more advanced checkpointers that support external databases.\n",
        "\n",
        "Here, we'll show how to use [Sqlite as a checkpointer](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer), but other checkpointers, such as [Postgres](https://langchain-ai.github.io/langgraph/how-tos/persistence_postgres/) are available!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "85ed78d9-6ca2-45ac-96a9-52e341ec519d",
      "metadata": {
        "id": "85ed78d9-6ca2-45ac-96a9-52e341ec519d"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install --quiet -U langgraph-checkpoint-sqlite langchain_core langgraph langchain_google_genai"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "2e10c4d4",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "2e10c4d4",
        "outputId": "a400e97e-05a7-4ef8-f34b-a6ddfba84cc0"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "env: GOOGLE_API_KEY=AIzaSyCD_du_wlnoyfrBSnS9k-fyqcs-C2VKqI4\n",
            "AIzaSyCD_du_wlnoyfrBSnS9k-fyqcs-C2VKqI4\n"
          ]
        }
      ],
      "source": [
        "from google.colab import userdata\n",
        "\n",
        "%env GOOGLE_API_KEY = {userdata.get('GEMINI_API_KEY')}\n",
        "\n",
        "import os\n",
        "print(os.environ[\"GOOGLE_API_KEY\"])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "6a002315",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6a002315",
        "outputId": "802d4e6d-cd6e-4995-f2d8-de89ae89cb35"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "env: LANGCHAIN_API_KEY=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\n"
          ]
        }
      ],
      "source": [
        "%env LANGCHAIN_API_KEY = {userdata.get('LANGCHAIN_API_KEY')}\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b40d25c0-e9b5-4854-bf07-3cc3ff07122e",
      "metadata": {
        "id": "b40d25c0-e9b5-4854-bf07-3cc3ff07122e"
      },
      "source": [
        "## Sqlite\n",
        "\n",
        "A good starting point here is the [SqliteSaver checkpointer](https://langchain-ai.github.io/langgraph/concepts/low_level/#checkpointer).\n",
        "\n",
        "Sqlite is a [small, fast, highly popular](https://x.com/karpathy/status/1819490455664685297) SQL database.\n",
        "\n",
        "If we supply `\":memory:\"` it creates an in-memory Sqlite database."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "fae15402-17ae-4e89-8ecf-4c89e08b22fe",
      "metadata": {
        "id": "fae15402-17ae-4e89-8ecf-4c89e08b22fe"
      },
      "outputs": [],
      "source": [
        "import sqlite3\n",
        "# In memory\n",
        "conn = sqlite3.connect(\":memory:\", check_same_thread = False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c2bf53ec-6d4a-42ce-8183-344795eed403",
      "metadata": {
        "id": "c2bf53ec-6d4a-42ce-8183-344795eed403"
      },
      "source": [
        "But, if we supply a db path, then it will create a database for us!"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "58339167-920c-4994-a0a7-0a9c5d4f7cf7",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "58339167-920c-4994-a0a7-0a9c5d4f7cf7",
        "outputId": "6ec6961d-9c61-4cfd-80e7-3ba5ee6ab247"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "--2024-09-25 11:38:01--  https://github.com/langchain-ai/langchain-academy/raw/main/module-2/state_db/example.db\n",
            "Resolving github.com (github.com)... 140.82.114.4\n",
            "Connecting to github.com (github.com)|140.82.114.4|:443... connected.\n",
            "HTTP request sent, awaiting response... 302 Found\n",
            "Location: https://raw.githubusercontent.com/langchain-ai/langchain-academy/main/module-2/state_db/example.db [following]\n",
            "--2024-09-25 11:38:01--  https://raw.githubusercontent.com/langchain-ai/langchain-academy/main/module-2/state_db/example.db\n",
            "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n",
            "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n",
            "HTTP request sent, awaiting response... 200 OK\n",
            "Length: 110592 (108K) [application/octet-stream]\n",
            "Saving to: ‘state_db/example.db’\n",
            "\n",
            "example.db          100%[===================>] 108.00K  --.-KB/s    in 0.003s  \n",
            "\n",
            "2024-09-25 11:38:01 (36.6 MB/s) - ‘state_db/example.db’ saved [110592/110592]\n",
            "\n"
          ]
        }
      ],
      "source": [
        "# pull file if it doesn't exist and connect to local db\n",
        "!mkdir -p state_db && [ ! -f state_db/example.db ] && wget -P state_db https://github.com/langchain-ai/langchain-academy/raw/main/module-2/state_db/example.db\n",
        "\n",
        "db_path = \"state_db/example.db\"\n",
        "conn = sqlite3.connect(db_path, check_same_thread=False)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "3c7736b6-a750-48f8-a838-8e7616b12250",
      "metadata": {
        "id": "3c7736b6-a750-48f8-a838-8e7616b12250"
      },
      "outputs": [],
      "source": [
        "# Here is our checkpointer\n",
        "from langgraph.checkpoint.sqlite import SqliteSaver\n",
        "memory: SqliteSaver = SqliteSaver(conn)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9d8cb629-213f-4b87-965e-19b812c42da1",
      "metadata": {
        "id": "9d8cb629-213f-4b87-965e-19b812c42da1"
      },
      "source": [
        "Let's re-define our chatbot."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "dc414e29-2078-41a0-887c-af1a6a3d72c0",
      "metadata": {
        "id": "dc414e29-2078-41a0-887c-af1a6a3d72c0"
      },
      "outputs": [],
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "from langchain_core.messages import SystemMessage, HumanMessage, RemoveMessage\n",
        "\n",
        "from langgraph.graph import END\n",
        "from langgraph.graph import MessagesState\n",
        "\n",
        "model: ChatGoogleGenerativeAI = ChatGoogleGenerativeAI(model = \"gemini-1.5-flash\")\n",
        "\n",
        "class State(MessagesState):\n",
        "    summary: str\n",
        "\n",
        "# Define the logic to call the model\n",
        "def call_model(state: State) -> State:\n",
        "\n",
        "    # Get summary if it exists\n",
        "    summary = state.get(\"summary\", \"\")\n",
        "\n",
        "    # If there is summary, then we add it\n",
        "    if summary:\n",
        "\n",
        "        # Add summary to system message\n",
        "        system_message = f\"Summary of conversation earlier: {summary}\"\n",
        "\n",
        "        # Append summary to any newer messages\n",
        "        messages = [SystemMessage(content=system_message)] + state[\"messages\"]\n",
        "\n",
        "    else:\n",
        "        messages = state[\"messages\"]\n",
        "\n",
        "    response = model.invoke(messages)\n",
        "    return {\"messages\": response}\n",
        "\n",
        "def summarize_conversation(state: State) -> State:\n",
        "\n",
        "    # First, we get any existing summary\n",
        "    summary = state.get(\"summary\", \"\")\n",
        "\n",
        "    # Create our summarization prompt\n",
        "    if summary:\n",
        "\n",
        "        # A summary already exists\n",
        "        summary_message = (\n",
        "            f\"This is summary of the conversation to date: {summary}\\n\\n\"\n",
        "            \"Extend the summary by taking into account the new messages above:\"\n",
        "        )\n",
        "\n",
        "    else:\n",
        "        summary_message = \"Create a summary of the conversation above:\"\n",
        "\n",
        "    # Add prompt to our history\n",
        "    messages = state[\"messages\"] + [HumanMessage(content=summary_message)]\n",
        "    response = model.invoke(messages)\n",
        "\n",
        "    # Delete all but the 2 most recent messages\n",
        "    delete_messages = [RemoveMessage(id=m.id) for m in state[\"messages\"][:-2]]\n",
        "    return {\"summary\": response.content, \"messages\": delete_messages}\n",
        "\n",
        "# Determine whether to end or summarize the conversation\n",
        "def should_continue(state: State) -> State:\n",
        "\n",
        "    \"\"\"Return the next node to execute.\"\"\"\n",
        "\n",
        "    messages = state[\"messages\"]\n",
        "\n",
        "    # If there are more than six messages, then we summarize the conversation\n",
        "    if len(messages) > 6:\n",
        "        return \"summarize_conversation\"\n",
        "\n",
        "    # Otherwise we can just end\n",
        "    return END"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41c13c0b-a383-4f73-9cc1-63f0eed8f190",
      "metadata": {
        "id": "41c13c0b-a383-4f73-9cc1-63f0eed8f190"
      },
      "source": [
        "Now, we just re-compile with our sqlite checkpointer."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e867fd95-91eb-4ce1-82fc-bb72d611a96d",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 350
        },
        "id": "e867fd95-91eb-4ce1-82fc-bb72d611a96d",
        "outputId": "2ca033b1-00c7-47e5-af2a-e91c786e2f5a"
      },
      "outputs": [
        {
          "data": {
            "image/jpeg": "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",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from IPython.display import Image, display\n",
        "from langgraph.graph import StateGraph, START\n",
        "from langgraph.graph.state import CompiledStateGraph\n",
        "\n",
        "# Define a new graph\n",
        "workflow: StateGraph = StateGraph(State)\n",
        "workflow.add_node(\"conversation\", call_model)\n",
        "workflow.add_node(summarize_conversation)\n",
        "\n",
        "# Set the entrypoint as conversation\n",
        "workflow.add_edge(START, \"conversation\")\n",
        "workflow.add_conditional_edges(\"conversation\", should_continue)\n",
        "workflow.add_edge(\"summarize_conversation\", END)\n",
        "\n",
        "# Compile\n",
        "graph: CompiledStateGraph = workflow.compile(checkpointer=memory)\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8769db99-3938-45e6-a594-56beb18d6c45",
      "metadata": {
        "id": "8769db99-3938-45e6-a594-56beb18d6c45"
      },
      "source": [
        "Now, we can invoke the graph several times."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "0f4094a0-d240-4be8-903a-7d9f605bdc5c",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0f4094a0-d240-4be8-903a-7d9f605bdc5c",
        "outputId": "9ab4170f-6563-4fff-8533-82b32125c2a6"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Hi Lance! It's nice to hear from you again. It seems you're a big fan of the 49ers. We were just talking about them. Is there something specific you'd like to chat about regarding the team? We could discuss their current roster, their history, or even some of their most memorable games. What interests you most about the 49ers?\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "Your name is Lance! You told me that at the beginning of our conversation. 😊  \n",
            "\n",
            "Do you want to talk about the 49ers? I'm happy to discuss their history, their current roster, or even some of their most memorable games.\n",
            "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
            "\n",
            "I hear you! The 49ers have a lot of passionate fans. What specifically do you like about them? Are you a fan of their current roster, or do you have a favorite player from the past?\n"
          ]
        }
      ],
      "source": [
        "# Create a thread\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "\n",
        "# Start conversation\n",
        "input_message = HumanMessage(content=\"hi! I'm Lance\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "input_message = HumanMessage(content=\"what's my name?\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()\n",
        "\n",
        "input_message = HumanMessage(content=\"i like the 49ers!\")\n",
        "output = graph.invoke({\"messages\": [input_message]}, config)\n",
        "for m in output['messages'][-1:]:\n",
        "    m.pretty_print()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c0f3e842-4497-45e2-a924-69672a9bcb33",
      "metadata": {
        "id": "c0f3e842-4497-45e2-a924-69672a9bcb33"
      },
      "source": [
        "Let's confirm that our state is saved locally."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "d2ab158a-5a82-417a-8841-730a4cc18ea7",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "d2ab158a-5a82-417a-8841-730a4cc18ea7",
        "outputId": "4c173155-1d7d-4fae-8757-9c833eeab4ab"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "StateSnapshot(values={'messages': [HumanMessage(content=\"hi! I'm Lance\", additional_kwargs={}, response_metadata={}, id='281ecfdc-86c1-4ca5-9c4b-41ce2b48966e'), AIMessage(content=\"Hi Lance! It's nice to hear from you again. It seems you're a big fan of the 49ers. We were just talking about them. Is there something specific you'd like to chat about regarding the team? We could discuss their current roster, their history, or even some of their most memorable games. What interests you most about the 49ers? \\n\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-1b4d4c26-8466-477b-a102-58b8e7983635-0', usage_metadata={'input_tokens': 350, 'output_tokens': 80, 'total_tokens': 430}), HumanMessage(content=\"what's my name?\", additional_kwargs={}, response_metadata={}, id='31e9889c-f54c-40a7-a4e1-83cfccc442b2'), AIMessage(content=\"Your name is Lance! You told me that at the beginning of our conversation. 😊  \\n\\nDo you want to talk about the 49ers? I'm happy to discuss their history, their current roster, or even some of their most memorable games. \\n\", additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-8cfbf18d-553b-4e93-8fad-966e0819856c-0', usage_metadata={'input_tokens': 282, 'output_tokens': 53, 'total_tokens': 335}), HumanMessage(content='i like the 49ers!', additional_kwargs={}, response_metadata={}, id='05b52815-f495-452b-9976-34d6440a1d46'), AIMessage(content='I hear you! The 49ers have a lot of passionate fans. What specifically do you like about them? Are you a fan of their current roster, or do you have a favorite player from the past? \\n', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-f5ab9045-2b13-4192-9667-3646a8839bbf-0', usage_metadata={'input_tokens': 347, 'output_tokens': 44, 'total_tokens': 391})], 'summary': \"Here's an extended summary of the conversation:\\n\\nLance introduced himself twice during the conversation, expressing his fondness for the San Francisco 49ers football team. The AI assistant acknowledged Lance's name each time and showed willingness to discuss the 49ers, offering to talk about various aspects of the team such as their history, current roster, or memorable games.  The AI then provided a summary of the conversation so far, highlighting Lance's repeated introductions and lack of direct engagement with the AI's prompts about the 49ers. \\n\\nThe conversation was brief and somewhat repetitive, with Lance reintroducing himself at the end without directly responding to the AI's questions or prompts about the 49ers. It seems Lance may be struggling to engage in a back-and-forth conversation or may be unsure how to respond to the AI's prompts. \\n\"}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7b327-89bb-6c88-801b-c0cb386f00af'}}, metadata={'source': 'loop', 'writes': {'conversation': {'messages': AIMessage(content='I hear you! The 49ers have a lot of passionate fans. What specifically do you like about them? Are you a fan of their current roster, or do you have a favorite player from the past? \\n', additional_kwargs={}, response_metadata={'prompt_feedback': {'block_reason': 0, 'safety_ratings': []}, 'finish_reason': 'STOP', 'safety_ratings': [{'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HATE_SPEECH', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_HARASSMENT', 'probability': 'NEGLIGIBLE', 'blocked': False}, {'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'probability': 'NEGLIGIBLE', 'blocked': False}]}, id='run-f5ab9045-2b13-4192-9667-3646a8839bbf-0', usage_metadata={'input_tokens': 347, 'output_tokens': 44, 'total_tokens': 391})}}, 'step': 27, 'parents': {}}, created_at='2024-09-25T11:36:57.382795+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef7b327-855c-6983-801a-e36891ddda9b'}}, tasks=())"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "graph_state = graph.get_state(config)\n",
        "graph_state"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e21152d-ed9c-408d-b7d5-f634c9ce81e2",
      "metadata": {
        "id": "1e21152d-ed9c-408d-b7d5-f634c9ce81e2"
      },
      "source": [
        "### Persisting state\n",
        "\n",
        "Using database like Sqlite means state is persisted!\n",
        "\n",
        "For example, we can re-start the notebook kernel and see that we can still load from Sqlite DB on disk.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "b9a44dc5-be04-45fa-a6fc-27b0f8ee4678",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "b9a44dc5-be04-45fa-a6fc-27b0f8ee4678",
        "outputId": "887ade22-4304-4370-c654-d42e4183c068"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "StateSnapshot(values={'messages': [HumanMessage(content=\"hi! I'm Lance\", additional_kwargs={}, response_metadata={}, id='086e80ce-8bd0-4dee-b13c-f47160d6755f'), AIMessage(content=\"Hello Lance! You've already introduced yourself, but it's nice to greet you again. Is there something specific you'd like to talk about? Perhaps you'd like to continue our conversation about the 49ers, or is there another topic you're interested in discussing?\", additional_kwargs={}, response_metadata={'id': 'msg_01AkDF6AH7LMp2YDDD9jQkEC', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 197, 'output_tokens': 58}}, id='run-7f8af5e1-5248-4ccd-ab46-87580422f0d6-0', usage_metadata={'input_tokens': 197, 'output_tokens': 58, 'total_tokens': 255}), HumanMessage(content=\"what's my name?\", additional_kwargs={}, response_metadata={}, id='e33f42ee-cdb0-45ec-b100-54dcd4c82e71'), AIMessage(content=\"Your name is Lance. You've introduced yourself twice during our conversation.\", additional_kwargs={}, response_metadata={'id': 'msg_01LVJSCjRjmRd3QA2UMP6SXg', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 193, 'output_tokens': 17}}, id='run-4152603e-5c5f-4a26-be8c-2b2bab373e75-0', usage_metadata={'input_tokens': 193, 'output_tokens': 17, 'total_tokens': 210}), HumanMessage(content='i like the 49ers!', additional_kwargs={}, response_metadata={}, id='85513182-bfb9-445b-b6ff-a6553bf63767'), AIMessage(content=\"Yes, you've mentioned that you like the San Francisco 49ers! They're a popular NFL team with a rich history. Since you're a fan, is there anything specific about the 49ers you'd like to discuss? We could talk about:\\n\\n1. Their current roster and key players\\n2. The team's history and past achievements\\n3. Memorable games or seasons\\n4. Their prospects for the upcoming season\\n5. The team's rivalries\\n6. Their home stadium, Levi's Stadium\\n\\nWhat aspect of the 49ers interests you most?\", additional_kwargs={}, response_metadata={'id': 'msg_018FVfpp9brQykX53VK88tv5', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 220, 'output_tokens': 123}}, id='run-68b784a6-c518-4904-a208-6214b7223414-0', usage_metadata={'input_tokens': 220, 'output_tokens': 123, 'total_tokens': 343})], 'summary': \"Here's a summary of the conversation:\\n\\nLance introduced himself twice during the conversation. He expressed his fondness for the San Francisco 49ers football team. The AI assistant acknowledged Lance's name each time and showed willingness to discuss the 49ers, offering to talk about various aspects of the team such as their history, current roster, or memorable games. The conversation was brief and somewhat repetitive, with Lance reintroducing himself at the end without directly responding to the AI's questions or prompts about the 49ers.\"}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef64b23-31cd-655a-8011-d28d83f59223'}}, metadata={'source': 'loop', 'writes': {'conversation': {'messages': AIMessage(content=\"Yes, you've mentioned that you like the San Francisco 49ers! They're a popular NFL team with a rich history. Since you're a fan, is there anything specific about the 49ers you'd like to discuss? We could talk about:\\n\\n1. Their current roster and key players\\n2. The team's history and past achievements\\n3. Memorable games or seasons\\n4. Their prospects for the upcoming season\\n5. The team's rivalries\\n6. Their home stadium, Levi's Stadium\\n\\nWhat aspect of the 49ers interests you most?\", additional_kwargs={}, response_metadata={'id': 'msg_018FVfpp9brQykX53VK88tv5', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 220, 'output_tokens': 123}}, id='run-68b784a6-c518-4904-a208-6214b7223414-0', usage_metadata={'input_tokens': 220, 'output_tokens': 123, 'total_tokens': 343})}}, 'step': 17}, created_at='2024-08-27T20:23:19.625843+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef64b23-1a48-6402-8010-79e09364e17f'}}, tasks=())"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Create a thread\n",
        "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
        "graph_state = graph.get_state(config)\n",
        "graph_state"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8466e418-1a46-4cdb-a51a-6ae14281bb85",
      "metadata": {
        "id": "8466e418-1a46-4cdb-a51a-6ae14281bb85"
      },
      "source": [
        "## LangGraph Studio\n",
        "\n",
        "--\n",
        "\n",
        "**⚠️ DISCLAIMER**\n",
        "\n",
        "*Running Studio currently requires a Mac. If you are not using a Mac, then skip this step.*\n",
        "\n",
        "--\n",
        "\n",
        "Now that we better understand external memory, recall that the LangGraph API packages your code and provides you with with built-in persistence.\n",
        "\n",
        "And the API is the back-end for Studio!\n",
        "\n",
        "Load the `chatbot` in the UI, which uses `module2-/studio/chatbot.py` set in `module2-/studio/langgraph.json`."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c4916d8b",
      "metadata": {
        "id": "c4916d8b"
      },
      "source": []
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
